Aquí está el ejemplo:
if(value != ageValue) { ageValue = value; }Es decir, si asignamos el valor de una variable a otra, ¿por qué tendríamos que verificar si tienen el mismo valor de todos modos?
Eso me confunde. Aquí está el contexto más amplio:
private double ageValue; public double Age { get { return ageValue; } set { if(value != ageValue) { ageValue = value; } } }Sí, esto if es inútil. Comprueba si el valor es el mismo (y configúralo si no).
Cuando el operador != no está sobrecargado, entonces es esto:
private double ageValue; public double Age { get { return ageValue; } set { if (value != ageValue) { ageValue = value; } } }Lo mismo
private double ageValue; public double Age { get { return ageValue; } set { ageValue = value; } }El if es, en la inspección, no redundante. Depende de la implementación restante. Tenga en cuenta que en C#, != se puede sobrecargar, lo que significa que la evaluación puede tener efectos secundarios. Además, las variables marcadas podrían implementarse como propiedades, lo que también puede tener efectos secundarios en la evaluación.
En un control de winforms habíamos establecido el Color de fondo en un color específico:
myControl.BackgroundColor = Color.WhiteEn circunstancias específicas, esto podría ocurrir en un ciclo cerrado y conducir a una interfaz de usuario congelada. Después de un análisis de rendimiento, descubrimos que esta llamada era el motivo de la IU congelada, por lo que simplemente la cambiamos a:
if (myControl.BackgroundColor != Color.White) myControl.BackgroundColor = Color.WhiteY el rendimiento de nuestra herramienta volvió a la normalidad (y luego eliminamos la razón del ciclo cerrado).
Así que esta verificación no siempre es redundante. Especialmente si el objetivo es una propiedad que hace más dentro del setter que simplemente aplicar el valor a una tienda de respaldo.
Aquí hay una muestra de código cuando la verificación es bastante útil :
public class MyClass { ... int ageValue = 0; public int AgeValue { get { return ageValue } protected set { ... // value validation here // your code starts if (value != ageValue) { ageValue = value; } // your code ends else return; // do nothing since value == ageValue // ageValue has been changed // Time (or / and memory) consuming process SaveToRDBMS(); InvalidateCache(); ... } } ...Sin embargo, una implementación más natural es verificar desde el principio para evitar cálculos innecesarios.
protected set { if (ageValue == value) return; ... // value validation here ageValue = value; // ageValue has been changed // Time (or / and memory) consuming process SaveToRDBMS(); InvalidateCache(); ... }De hecho, he codificado cosas como esta varias veces, por diferentes razones. Son un poco difíciles de explicar, así que tengan paciencia conmigo.
Lo principal es que no establece una nueva referencia si el valor en la referencia es lógicamente igual al valor de la referencia anterior. En los comentarios anteriores, los usuarios han criticado lo detestable de este escenario, y es detestable tener que lidiar con él, pero sigue siendo esencialmente necesario en los casos.
Intentaría dividir casos de uso como este:
El valor es un tipo de datos abstracto, donde puede tener diferentes instancias construidas que representan el mismo valor lógico.
La referencia de value es útil para una lógica de almacenamiento en caché.
Está utilizando un evaluador reactivo, donde establecer un nuevo valor puede forzar una reacción en cadena de actualizaciones.
El gran punto conceptual es que, en algunos casos, puede tener el mismo valor lógico almacenado en diferentes referencias, pero desea intentar minimizar la cantidad de referencias degeneradas por dos grandes razones:
Tener el mismo valor lógico almacenado varias veces consume más memoria.
Gran parte del tiempo de ejecución puede usar la verificación de referencias como un atajo, por ejemplo, a través del almacenamiento en caché, que puede ser más eficiente si evita permitir que se propaguen referencias redundantes al mismo valor lógico.
Para otro ejemplo aleatorio, el recolector de basura de .NET es " generacional " , lo que significa que se esfuerza más en verificar si se puede recopilar un valor cuando es más nuevo. Por lo tanto, el recolector de elementos no utilizados puede experimentar ganancias si prefiere conservar la referencia anterior, ya que se encuentra en una generación más privilegiada, lo que permite que la referencia más nueva recopile los elementos no utilizados antes.
Otro caso de uso, de nuevo con tipos de datos abstractos, es donde puede tener propiedades evaluadas de forma perezosa adjuntas a ellos. Por ejemplo, supongamos que tiene un abstract class Number que tiene propiedades como .IsRational , .IsEven , etc. Entonces, es posible que no los calcule de inmediato, sino que los genere a pedido, almacenando en caché los resultados. En un escenario como este, es posible que tienda a preferir conservar los Number anteriores con el mismo valor lógico, ya que pueden tener más cosas adjuntas, mientras que un nuevo value puede tener menos información asociada, incluso si es lógicamente == .
Es un poco difícil pensar en cómo resumir las diversas razones por las que esto puede tener sentido en algunos casos, pero básicamente es una optimización que puede tener sentido si tiene una razón para usarla. Si no tiene ninguna razón para usarlo, probablemente sea mejor no preocuparse hasta que surja alguna motivación.
El rendimiento no es un gran problema, solo depende de sus necesidades lógicas.